
// INCLUDES ********************************************************************

#include "Tween.h"

#include "Logger.h"

#include <stdexcept>



// NAMESPACES ******************************************************************

using namespace Meteor;

#pragma mark -
#pragma mark Constructor
// -----------------------------------------------------------------------------

Tween::Tween( const TweenAttribute attr, const EaseType type,
              const float startValue, const float targetValue,
              const float duration)

: mAttribute( attr), mTargetValue( targetValue, 0.0f), mChange( targetValue-startValue, 0.0f),
  mDuration( duration*60), mStartValue( startValue), mEaseType( type), mFinished( false)
{
    if ( attr > NumAttributes)
    {
        throw std::invalid_argument( "Invalid TweenAttribute");
    }
    
    if ( type > NumEaseTypes)
    {
        throw std::invalid_argument( "Invalid EaseType");
    }
}

// -----------------------------------------------------------------------------

Tween::Tween( const TweenAttribute attr, const EaseType type,
              const Vector2D& startValue, const Vector2D& targetValue,
              const float duration)

: mAttribute( attr), mTargetValue( targetValue), mChange( targetValue-startValue),
  mDuration( duration*60), mStartValue( startValue), mEaseType( type), mFinished( false)
{

}

// -----------------------------------------------------------------------------

Meteor::Tween::Tween( const Tween& old)
{
    mAttribute = old.mAttribute;
    
    mTargetValue = old.mTargetValue;
    
    mChange = old.mChange;
    
    mDuration = old.mDuration;
    
    mStartValue = old.mStartValue;
    
    mFinished = false;
    
    mEaseType = old.mEaseType;
}

#pragma mark -
#pragma mark Default tween
// -----------------------------------------------------------------------------

const float Tween::None( const float time,
                         const float offset,
                         const float change,
                         const float duration)
{
    return time * ( change / duration) + offset;
}

// -----------------------------------------------------------------------------

const float Tween::In( const float time,
                       const float offset,
                       const float change,
                       const float duration)
{
    return None( time, offset, change, duration);
}

// -----------------------------------------------------------------------------

const float Tween::Out( const float time,
                        const float offset,
                        const float change,
                        const float duration)
{
    return None( time, offset, change, duration);
}

// -----------------------------------------------------------------------------

const float Tween::InOut( const float time,
                          const float offset,
                          const float change,
                          const float duration)
{
    return None( time, offset, change, duration);
}

#pragma mark -
#pragma mark Getter
// -----------------------------------------------------------------------------

const Tween::TweenAttribute Tween::Attribute() const
{
    return mAttribute;
}

// -----------------------------------------------------------------------------

const bool Tween::Finished() const
{
    return mFinished;
}


#pragma mark -
#pragma mark Finish
// -----------------------------------------------------------------------------

void Tween::IsFinished( const float value)
{
    if ( mChange.X() <= 0 && value <= mTargetValue.X())
    {
        mFinished = true;
    }
    else if ( mChange.X() > 0 && value >= mTargetValue.X())
    {
        mFinished = true;
    }
    else
    {
        mFinished = false;
    }
}

// -----------------------------------------------------------------------------

void Tween::IsFinished( const Vector2D& value)
{
    if ( mChange.X() <= 0 && value.X() <= mTargetValue.X() &&
         mChange.Y() <= 0 && value.Y() <= mTargetValue.Y())
    {
        mFinished = true;
    }
    else if ( mChange.X() > 0 && value.X() >= mTargetValue.X() &&
              mChange.Y() <= 0 && value.Y() <= mTargetValue.Y())
    {
        mFinished = true;
    }
    else if ( mChange.X() <= 0 && value.X() <= mTargetValue.X() &&
              mChange.Y() > 0 && value.Y() >= mTargetValue.Y())
    {
        mFinished = true;
    }
    else if ( mChange.X() > 0 && value.X() >= mTargetValue.X() &&
              mChange.Y() > 0 && value.Y() >= mTargetValue.Y())
    {
        mFinished = true;
    }
    else
    {
        mFinished = false;
    }
}


#pragma mark -
#pragma mark Update
// -----------------------------------------------------------------------------

const Vector2D Tween::Update( const float time)
{
    Vector2D returnValue;

    if ( mAttribute == Position)
    {
        returnValue.SetX( None( time, mStartValue.X(), mChange.X(), mDuration));
        
        returnValue.SetY( None( time, mStartValue.Y(), mChange.Y(), mDuration));

        IsFinished( returnValue);
    }
    else
    {
        returnValue.SetX( None( time, mStartValue.X(), mChange.X(), mDuration));

        IsFinished( returnValue.X());
    }
    
    return returnValue;
}
